<!DOCTYPE html>
<html>
    <head>
        <script src="/w3c/resources/testharness.js"></script>
        <script src="/w3c/resources/testharnessreport.js"></script>
        <script src="mediasource-util.js"></script>
        <link rel="stylesheet" href="/w3c/resources/testharness.css">
    </head>
    <body>
        <div id="log"></div>
        <script>
            var subType = MediaSourceUtil.getSubType(MediaSourceUtil.AUDIO_ONLY_TYPE);

            var manifestFilenameA = subType + '/test-a-128k-44100Hz-1ch-manifest.json';
            var manifestFilenameB = subType + '/test-v-128k-320x240-30fps-10kfr-manifest.json';

            var expectationsA = {
              webm: '{ [0.000, 2.044) }',
              mp4: '{ [0.000, 2.043) }',
            };

            var expectationsB = {
              webm: '{ [0.000, 2.000) }',
              mp4: '{ [0.000, 2.000) }',
            };

            function mediaSourceDemuxedTest(callback, description)
            {
                mediasource_test(function(test, mediaElement, mediaSource)
                {
                    mediaElement.pause();
                    test.failOnEvent(mediaElement, 'error');
                    test.endOnEvent(mediaElement, 'ended');

                    MediaSourceUtil.fetchManifestAndData(test, manifestFilenameA, function(typeA, dataA)
                    {
                        MediaSourceUtil.fetchManifestAndData(test, manifestFilenameB, function(typeB, dataB)
                        {
                            mediaSource.addSourceBuffer(typeA);
                            mediaSource.addSourceBuffer(typeB);
                            assert_equals(mediaSource.sourceBuffers.length, 2);

                            callback(test, mediaElement, mediaSource, dataA, dataB);
                        });
                    });
                }, description);
            };

            function appendDataAndVerifyAddedToActiveSourceBuffers(test, mediaSource, dataA, dataB, swapAppendBufferOrder, forceParsingOrder, callback)
            {
                // Verification here assumes no prior initialization segment received for any of mediaSource's sourceBuffers.
                assert_equals(mediaSource.sourceBuffers.length, 2, 'mediaSource sourceBuffers length before appends');
                assert_equals(mediaSource.activeSourceBuffers.length, 0, 'mediaSource activeSourceBuffers length before appends');
                var sourceBufferA = mediaSource.sourceBuffers[0];
                var sourceBufferB = mediaSource.sourceBuffers[1];

                var firstBuffer = swapAppendBufferOrder ? sourceBufferB : sourceBufferA;
                var firstData = swapAppendBufferOrder ? dataB : dataA;
                var secondBuffer = swapAppendBufferOrder ? sourceBufferA : sourceBufferB;
                var secondData = swapAppendBufferOrder ? dataA : dataB;

                test.expectEvent(firstBuffer, 'update');
                test.expectEvent(firstBuffer, 'updateend');
                firstBuffer.appendBuffer(firstData);

                var doSecondAppend = function()
                {
                    test.expectEvent(secondBuffer, 'update');
                    test.expectEvent(secondBuffer, 'updateend');
                    secondBuffer.appendBuffer(secondData);
                };

                if (forceParsingOrder) {
                    test.waitForExpectedEvents(function()
                    {
                        assert_equals(mediaSource.sourceBuffers.length, 2, 'mediaSource sourceBuffers length between completed appends');
                        assert_equals(mediaSource.activeSourceBuffers.length, 1, 'mediaSource activeSourceBuffers length between completed appends');
                        assert_equals(mediaSource.activeSourceBuffers[0], firstBuffer);
                        doSecondAppend();
                    });
                } else {
                    doSecondAppend();
                }

                test.waitForExpectedEvents(function()
                {
                    assert_equals(mediaSource.sourceBuffers.length, 2, 'mediaSource sourceBuffers length after appends completed');
                    assert_equals(mediaSource.activeSourceBuffers.length, 2, 'mediaSource activeSourceBuffers length after appends completed');
                    assert_equals(mediaSource.activeSourceBuffers[0], sourceBufferA);
                    assert_equals(mediaSource.activeSourceBuffers[1], sourceBufferB);
                    callback();
                });
            }

            mediaSourceDemuxedTest(function(test, mediaElement, mediaSource, dataA, dataB) {
                appendDataAndVerifyAddedToActiveSourceBuffers(test, mediaSource, dataA, dataB, false, false, function()
                {
                    assertBufferedEquals(mediaSource.activeSourceBuffers[0], expectationsA[subType], 'mediaSource.activeSourceBuffers[0]');
                    assertBufferedEquals(mediaSource.activeSourceBuffers[1], expectationsB[subType], 'mediaSource.activeSourceBuffers[1]');
                    assertBufferedEquals(mediaElement, expectationsB[subType], 'mediaElement.buffered');

                    mediaSource.endOfStream();

                    assertBufferedEquals(mediaSource.activeSourceBuffers[0], expectationsA[subType], 'mediaSource.activeSourceBuffers[0]');
                    assertBufferedEquals(mediaSource.activeSourceBuffers[1], expectationsB[subType], 'mediaSource.activeSourceBuffers[1]');
                    assertBufferedEquals(mediaElement, expectationsA[subType], 'mediaElement.buffered');

                    test.done();
                });
            }, 'Demuxed content with different lengths');

            mediaSourceDemuxedTest(function(test, mediaElement, mediaSource, dataA, dataB) {
                appendDataAndVerifyAddedToActiveSourceBuffers(test, mediaSource, dataA, dataB, false, true, function()
                {
                    assertBufferedEquals(mediaSource.activeSourceBuffers[0], expectationsA[subType], 'mediaSource.activeSourceBuffers[0]');
                    assertBufferedEquals(mediaSource.activeSourceBuffers[1], expectationsB[subType], 'mediaSource.activeSourceBuffers[1]');
                    assertBufferedEquals(mediaElement, expectationsB[subType], 'mediaElement.buffered');
                    test.done();
                });
            }, 'Process first init segment for sourceBuffer[0] first');

            mediaSourceDemuxedTest(function(test, mediaElement, mediaSource, dataA, dataB) {
                appendDataAndVerifyAddedToActiveSourceBuffers(test, mediaSource, dataA, dataB, true, true, function()
                {
                    assertBufferedEquals(mediaSource.activeSourceBuffers[0], expectationsA[subType], 'mediaSource.activeSourceBuffers[0]');
                    assertBufferedEquals(mediaSource.activeSourceBuffers[1], expectationsB[subType], 'mediaSource.activeSourceBuffers[1]');
                    assertBufferedEquals(mediaElement, expectationsB[subType], 'mediaElement.buffered');
                    test.done();
                });
            }, 'Process first init segment for sourceBuffer[1] first');

            mediasource_test(function(test, mediaElement, mediaSource)
            {
                mediaElement.pause();
                test.failOnEvent(mediaElement, 'error');
                test.endOnEvent(mediaElement, 'ended');

                MediaSourceUtil.fetchManifestAndData(test, subType + '/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr-manifest.json', function(type, data)
                {
                    var sourceBuffer = mediaSource.addSourceBuffer(type);
                    test.expectEvent(sourceBuffer, 'update');
                    test.expectEvent(sourceBuffer, 'updateend');
                    sourceBuffer.appendBuffer(data);

                    test.waitForExpectedEvents(function()
                    {
                        var expectationsAV = {
                            webm: ['{ [0.000, 2.003) }', '{ [0.000, 2.044) }'],
                            mp4: ['{ [0.000, 2.000) }', '{ [0.000, 2.043) }'],
                        };

                        var expectedBeforeEndOfStream = expectationsAV[subType][0];
                        var expectedAfterEndOfStream = expectationsAV[subType][1];

                        assertBufferedEquals(mediaSource.activeSourceBuffers[0], expectedBeforeEndOfStream, 'mediaSource.activeSourceBuffers[0]');
                        assertBufferedEquals(mediaElement, expectedBeforeEndOfStream, 'mediaElement.buffered');

                        mediaSource.endOfStream();

                        assertBufferedEquals(mediaSource.activeSourceBuffers[0], expectedAfterEndOfStream, 'mediaSource.activeSourceBuffers[0]');
                        assertBufferedEquals(mediaElement, expectedAfterEndOfStream, 'mediaElement.buffered');

                        test.done();
                    });
                });
            }, 'Muxed tracks with different lengths');

            mediaSourceDemuxedTest(function(test, mediaElement, mediaSource, dataA, dataB) {
                appendDataAndVerifyAddedToActiveSourceBuffers(test, mediaSource, dataA, dataB.subarray(0, 318), false, false, function()
                {
                    assertBufferedEquals(mediaSource.activeSourceBuffers[0], expectationsA[subType], 'mediaSource.activeSourceBuffers[0]');
                    assertBufferedEquals(mediaSource.activeSourceBuffers[1], '{ }', 'mediaSource.activeSourceBuffers[1]');
                    assertBufferedEquals(mediaElement, '{ }', 'mediaElement.buffered');

                    mediaSource.endOfStream();

                    assertBufferedEquals(mediaSource.activeSourceBuffers[0], expectationsA[subType], 'mediaSource.activeSourceBuffers[0]');
                    assertBufferedEquals(mediaSource.activeSourceBuffers[1], '{ }', 'mediaSource.activeSourceBuffers[1]');
                    assertBufferedEquals(mediaElement, '{ }', 'mediaElement.buffered');

                    test.done();
                });
            }, 'Demuxed content with an empty buffered range on one SourceBuffer');

            mediasource_test(function(test, mediaElement, mediaSource)
            {
                mediaElement.pause();
                test.failOnEvent(mediaElement, 'error');
                test.endOnEvent(mediaElement, 'ended');

                MediaSourceUtil.fetchManifestAndData(test, subType + '/test-av-384k-44100Hz-1ch-320x240-30fps-10kfr-manifest.json', function(type, data)
                {
                    var sourceBuffer = mediaSource.addSourceBuffer(type);
                    test.expectEvent(sourceBuffer, 'update');
                    test.expectEvent(sourceBuffer, 'updateend');
                    sourceBuffer.appendBuffer(data.subarray(0, 4052));

                    test.waitForExpectedEvents(function()
                    {
                        assertBufferedEquals(mediaSource.activeSourceBuffers[0], '{ }', 'mediaSource.activeSourceBuffers[0]');
                        assertBufferedEquals(mediaElement, '{ }', 'mediaElement.buffered');

                        mediaSource.endOfStream();

                        assertBufferedEquals(mediaSource.activeSourceBuffers[0], '{ }', 'mediaSource.activeSourceBuffers[0]');
                        assertBufferedEquals(mediaElement, '{ }', 'mediaElement.buffered');

                        test.done();
                    });
                });
            }, 'Muxed content empty buffered ranges.');

            mediasource_test(function(test, mediaElement, mediaSource)
            {
                mediaElement.pause();
                test.failOnEvent(mediaElement, 'error');
                test.endOnEvent(mediaElement, 'ended');

                var sourceBuffer = mediaSource.addSourceBuffer(MediaSourceUtil.AUDIO_ONLY_TYPE);

                assertBufferedEquals(mediaSource.sourceBuffers[0], '{ }', 'mediaSource.sourceBuffers[0]');
                assertBufferedEquals(mediaElement, '{ }', 'mediaElement.buffered');
                test.done();

            }, 'Get buffered range when sourcebuffer is empty.');

            mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
            {
                var initSegment = MediaSourceUtil.extractSegmentData(mediaData, segmentInfo.init);

                test.expectEvent(sourceBuffer, 'updateend', 'initSegment append ended.');
                sourceBuffer.appendBuffer(initSegment);
                test.waitForExpectedEvents(function()
                {
                    assertBufferedEquals(mediaSource.sourceBuffers[0], '{ }', 'mediaSource.sourceBuffers[0]');
                    assertBufferedEquals(mediaSource.activeSourceBuffers[0], '{ }', 'mediaSource.activeSourceBuffers[0]');
                    assertBufferedEquals(mediaElement, '{ }', 'mediaElement.buffered');
                    test.done();
                });

            }, 'Get buffered range when only initsegment is appended.');

            mediasource_testafterdataloaded(function(test, mediaElement, mediaSource, segmentInfo, sourceBuffer, mediaData)
            {
                test.expectEvent(mediaSource.sourceBuffers, 'removesourcebuffer', 'SourceBuffer removed.');
                mediaSource.removeSourceBuffer(sourceBuffer);

                test.waitForExpectedEvents(function()
                {
                    assert_throws_dom('InvalidStateError',
                        function() { sourceBuffer.buffered; },
                        'get sourceBuffer.buffered throws an exception for InvalidStateError.');
                    test.done();
                });
            }, 'Get buffered range after removing sourcebuffer.');
        </script>
    </body>
</html>
